package com.flame.auth.client.interceptor;

import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.flame.auth.client.config.AuthClientConfigurationProperties;
import com.flame.auth.core.config.AuthConfigurationProperties;
import com.flame.auth.core.constants.AuthConstants;
import com.flame.auth.core.context.AuthContextHolder;
import com.flame.auth.core.entity.TrustedPrincipal;
import com.flame.auth.core.exception.AuthExceptionMessage;
import com.flame.auth.core.exception.AuthRuntimeException;
import com.flame.core.utils.CipherUtils;
import com.flame.core.utils.DateUtils;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

public class TokenInterceptor implements HandlerInterceptor {

    private AuthConfigurationProperties authProperties;

    private AuthClientConfigurationProperties authClientProperties;

    public TokenInterceptor(AuthConfigurationProperties authProperties,
        AuthClientConfigurationProperties authClientProperties) {
        this.authProperties = authProperties;
        this.authClientProperties = authClientProperties;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        // 1、校验白名单
        String servletPath = request.getServletPath();
        AntPathMatcher matcher = new AntPathMatcher();
        for (String url : authProperties.getWhiteList()) {
            if (matcher.match(url, servletPath)) {
                return true;
            }
        }

        try {
            // 2、校验 token、刷新 token
            String token = request.getHeader(AuthConstants.TOKEN_HEADER);
            if (!StringUtils.hasText(token)) {
                throw new AuthRuntimeException(AuthExceptionMessage.ACCOUNT_LOGIN_TIMEOUT);
            }
            String decryptToken = CipherUtils.sm4Decrypt(token);
            if (!decryptToken.startsWith(AuthConstants.SALT)) {
                throw new AuthRuntimeException(AuthExceptionMessage.ACCOUNT_LOGIN_TIMEOUT);
            }
            String[] arr = decryptToken.replace(AuthConstants.SALT, "").split("_");
            String account = arr[0];
            long creatTimestamp = Long.parseLong(arr[1]);

            // 3、检测 token 是否过期，并把用户信息放到上下文中
            TrustedPrincipal trustedPrincipal =
                (TrustedPrincipal) request.getSession().getAttribute(AuthConstants.TRUSTED_PRINCIPAL);
            if (DateUtils.diff(creatTimestamp, System.currentTimeMillis(), TimeUnit.MINUTES) >
                authClientProperties.getTokenTimeout().toMinutes() || trustedPrincipal == null) {
                request.getSession().invalidate();
                throw new AuthRuntimeException(AuthExceptionMessage.ACCOUNT_LOGIN_TIMEOUT);
            }
            AuthContextHolder.setContext(trustedPrincipal);

            // 4、token刷新
            if (DateUtils.diff(creatTimestamp, System.currentTimeMillis(), TimeUnit.MINUTES) >=
                authClientProperties.getTokenRefresh().toMinutes()) {
                String refreshedToken =
                    CipherUtils.sm4Encrypt(AuthConstants.SALT + account + "_" + System.currentTimeMillis());
                response.addHeader(AuthConstants.TOKEN_HEADER, refreshedToken);
            }
        } catch (AuthRuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new AuthRuntimeException(AuthExceptionMessage.TOKEN_INVALIDE, e);
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
        throws Exception {
        AuthContextHolder.clear();
    }
}
